package timetabling;

import common.RandomGenerator;

import generalEvolutionaryAlgorithm.EAInstance;
import generalEvolutionaryAlgorithm.EAMutation;

public class TimetablingMutator2 implements EAMutation {
	private boolean[][] studentAttendsEvent;
	private boolean[][] eventCanTakePartInRoom;
	private int events, rooms;
	public TimetablingMutator2(boolean[][] studentAttendsEvent, boolean[][] eventCanTakePartInRoom) {
		this.studentAttendsEvent = studentAttendsEvent;
		this.eventCanTakePartInRoom = eventCanTakePartInRoom;
		events = this.studentAttendsEvent[0].length;
		rooms = this.eventCanTakePartInRoom[0].length;
	}

	@Override
	public EAInstance mutate(EAInstance eaInstance) {
		int operations = RandomGenerator.getPoissonVariable() + 1;
		for(int operation = 0; operation < operations; ++operation){
			eaInstance = singleMutation(eaInstance);
		}
		return eaInstance;
	}

	private EAInstance singleMutation(EAInstance eaInstance){
		int days = TimetablingConstants.DAYS;
		int hours = TimetablingConstants.HOURS;
		int spots = days * hours;
		TimetablingInstance instance = (TimetablingInstance) eaInstance;
		int[] roomForEvent = instance.getRoomForEvent();
		int[] timeForEvent = instance.getTimeForEvent();
		int[][] lecturesInRooms = new int[rooms][spots];
		for(int event = 0; event < events; event++){
			if(roomForEvent[event] != -1){
				lecturesInRooms[roomForEvent[event]][timeForEvent[event]]++;
			}
		}
		if(RandomGenerator.getInt(2) == 0){
			for(int i = 0; i < events; i++){
				int cycleLength = RandomGenerator.getPoissonVariable() + 2;
				int eventlist[] = new int[cycleLength];
				TimetablingInstance newInstance = generateInstanceRecursive(0, eventlist, timeForEvent, roomForEvent, rooms, events, eventCanTakePartInRoom, instance);
				if(newInstance != null){
					return newInstance;
				}
			}
		}
		while(true){
			int r = RandomGenerator.getInt(rooms);
			int e = RandomGenerator.getInt(events);
			for(int room = 0; room < rooms; ++room){
				++r;
				if(r == rooms)r = 0;
				for(int event = 0; event < events; ++event){
					++e;
					if(e == events)e = 0;
					if(eventCanTakePartInRoom[e][r]){
						if(r == roomForEvent[e]){
							roomForEvent[e] = -1;
							timeForEvent[e] = -1;
							return new TimetablingInstance(instance.getFunction(), timeForEvent, roomForEvent, rooms);
						} else {
							int t = RandomGenerator.getInt(spots);
							for(int time = 0; time < spots; time++){
								++t;
								if(t == spots)t = 0;
								if(lecturesInRooms[r][t] == 0){
									roomForEvent[e] = r;
									timeForEvent[e] = t;
									return  new TimetablingInstance(instance.getFunction(), timeForEvent, roomForEvent, rooms);
									//				tmp.calculateResult();
									//				System.out.println(tmp.toString() + tmp.getResult().optimalValue.toString());
								}
							}
						}
					}
				}
			}
		}
	}

	private TimetablingInstance generateInstanceRecursive(int index,
			int[] eventlist, int[] timeForEvent, int[] roomForEvent,
			int rooms, int events, boolean[][] eventCanTakePartInRoom, TimetablingInstance instance) {
		if(eventlist.length < 2){
			return null;
		}
		for(int z = 0; z < events; z++){
			int nextEvent = RandomGenerator.getInt(events);
			boolean ok = true;
			for(int j = 0; j < index; j++){
				if(nextEvent == eventlist[j]){
					ok = false;
				}
			}
			if(!ok)continue;
			if(timeForEvent[nextEvent] == -1 || roomForEvent[nextEvent] == -1){
				continue;
			}
			eventlist[index] = nextEvent;
			if(index > 0){
				if(eventCanTakePartInRoom[eventlist[index - 1]][roomForEvent[eventlist[index]]]){
					if(index == eventlist.length - 1){
						if(eventlist[index] == -1){
							System.out.println("A");
						}
						if(eventlist[0] == -1){
							System.out.println("B");
						}
						if(roomForEvent[eventlist[0]] == -1){
							System.out.println("C");
						}
						if(eventlist[index] == -1){
							System.out.println("D");
						}
						if(eventCanTakePartInRoom[eventlist[index]][roomForEvent[eventlist[0]]]){
							int tmp = roomForEvent[eventlist[0]];
							for(int i = 0; i < index; i++){
								roomForEvent[eventlist[i]] = roomForEvent[eventlist[i+1]];
							}
							roomForEvent[eventlist[index]] = tmp;
							
							tmp = timeForEvent[eventlist[0]];
							for(int i = 0; i < index; i++){
								timeForEvent[eventlist[i]] = timeForEvent[eventlist[i+1]];
							}
							timeForEvent[eventlist[index]] = tmp;
							return  new TimetablingInstance(instance.getFunction(), timeForEvent, roomForEvent, rooms);
						}
					} else {
						return generateInstanceRecursive(index + 1, eventlist, timeForEvent, roomForEvent, rooms, events, eventCanTakePartInRoom, instance);
					}
				}
			} else {
				return generateInstanceRecursive(index + 1, eventlist, timeForEvent, roomForEvent, rooms, events, eventCanTakePartInRoom, instance);
			}
		}
		return null;
	}
}
